/***************************************************************************\
 * The Mattise Kernel														*
 * Copyright 2007 Matthew Iselin											*
 * Licensed under the GPL													*
 *																			*
 * fault.cc																	*
 *																			*
 * Fault handlers, for such things as GPFs									*
 *																			*
\***************************************************************************/

#include <console.h>
#include <system.h>
#include <stdio.h>
#include <winman.h>
#include <gui.h>
#include <task.h>
#include <paging.h>
#include <syscall.h>

// sets an idt entry
void idt_set_gate(unsigned char num, unsigned long base, unsigned short sel, unsigned char flags);

// fault handler
extern "C" void fault_handler(struct regs *r);

/* These are function prototypes for all of the exception
*  handlers: The first 32 entries in the IDT are reserved
*  by Intel, and are designed to service exceptions! */
extern "C" {
extern void isr0();
extern void isr1();
extern void isr2();
extern void isr3();
extern void isr4();
extern void isr5();
extern void isr6();
extern void isr7();
extern void isr8();
extern void isr9();
extern void isr10();
extern void isr11();
extern void isr12();
extern void isr13();
extern void isr14();
extern void isr15();
extern void isr16();
extern void isr17();
extern void isr18();
extern void isr19();
extern void isr20();
extern void isr21();
extern void isr22();
extern void isr23();
extern void isr24();
extern void isr25();
extern void isr26();
extern void isr27();
extern void isr28();
extern void isr29();
extern void isr30();
extern void isr31();
extern void isr128(); // syscall, int 0x80
};

/* This is a very repetitive function... it's not hard, it's
*  just annoying. As you can see, we set the first 32 entries
*  in the IDT to the first 32 ISRs. We can't use a for loop
*  for this, because there is no way to get the function names
*  that correspond to that given entry. We set the access
*  flags to 0x8E. This means that the entry is present, is
*  running in ring 0 (kernel level), and has the lower 5 bits
*  set to the required '14', which is represented by 'E' in
*  hex. */
void isrs_install()
{
    idt_set_gate(0, (unsigned)isr0, 0x08, 0x8E);
    idt_set_gate(1, (unsigned)isr1, 0x08, 0x8E);
    idt_set_gate(2, (unsigned)isr2, 0x08, 0x8E);
    idt_set_gate(3, (unsigned)isr3, 0x08, 0x8E);
    idt_set_gate(4, (unsigned)isr4, 0x08, 0x8E);
    idt_set_gate(5, (unsigned)isr5, 0x08, 0x8E);
    idt_set_gate(6, (unsigned)isr6, 0x08, 0x8E);
    idt_set_gate(7, (unsigned)isr7, 0x08, 0x8E);

    idt_set_gate(8, (unsigned)isr8, 0x08, 0x8E);
    idt_set_gate(9, (unsigned)isr9, 0x08, 0x8E);
    idt_set_gate(10, (unsigned)isr10, 0x08, 0x8E);
    idt_set_gate(11, (unsigned)isr11, 0x08, 0x8E);
    idt_set_gate(12, (unsigned)isr12, 0x08, 0x8E);
    idt_set_gate(13, (unsigned)isr13, 0x08, 0x8E);
    idt_set_gate(14, (unsigned)isr14, 0x08, 0x8E);
    idt_set_gate(15, (unsigned)isr15, 0x08, 0x8E);

    idt_set_gate(16, (unsigned)isr16, 0x08, 0x8E);
    idt_set_gate(17, (unsigned)isr17, 0x08, 0x8E);
    idt_set_gate(18, (unsigned)isr18, 0x08, 0x8E);
    idt_set_gate(19, (unsigned)isr19, 0x08, 0x8E);
    idt_set_gate(20, (unsigned)isr20, 0x08, 0x8E);
    idt_set_gate(21, (unsigned)isr21, 0x08, 0x8E);
    idt_set_gate(22, (unsigned)isr22, 0x08, 0x8E);
    idt_set_gate(23, (unsigned)isr23, 0x08, 0x8E);

    idt_set_gate(24, (unsigned)isr24, 0x08, 0x8E);
    idt_set_gate(25, (unsigned)isr25, 0x08, 0x8E);
    idt_set_gate(26, (unsigned)isr26, 0x08, 0x8E);
    idt_set_gate(27, (unsigned)isr27, 0x08, 0x8E);
    idt_set_gate(28, (unsigned)isr28, 0x08, 0x8E);
    idt_set_gate(29, (unsigned)isr29, 0x08, 0x8E);
    idt_set_gate(30, (unsigned)isr30, 0x08, 0x8E);
    idt_set_gate(31, (unsigned)isr31, 0x08, 0x8E);
	
	// setup the syscall
    idt_set_gate(128, (unsigned)isr128, 0x08, 0x8E);
}

/* This is a simple string array. It contains the message that
*  corresponds to each and every exception. We get the correct
*  message by accessing like:
*  exception_message[interrupt_number] */
const char *exception_messages[] =
{
    "Division By Zero",
    "Debug",
    "Non Maskable Interrupt",
    "Breakpoint",
    "Into Detected Overflow",
    "Out of Bounds",
    "Invalid Opcode",
    "No Coprocessor",

    "Double Fault",
    "Coprocessor Segment Overrun",
    "Bad TSS",
    "Segment Not Present",
    "Stack Fault",
    "General Protection Fault",
    "Page Fault",
    "Unknown Interrupt",

    "Coprocessor Fault",
    "Alignment Check",
    "Machine Check",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",

    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved",
    "Reserved"
};

/* All of our Exception handling Interrupt Service Routines will
*  point to this function. This will tell us what exception has
*  happened! Right now, we simply halt the system by hitting an
*  endless loop. All ISRs disable interrupts while they are being
*  serviced as a 'locking' mechanism to prevent an IRQ from
*  happening and messing up kernel data structures */
void fault_handler(struct regs *r)
{
	// check if it's a GPF and we're in V8086 mode
	if( r->eflags & 0x20000 && r->int_no == 13 )
	{		
		// the IVT
		uint16_t* ivt = (uint16_t*) 0;
		
		// the stack, in 16-bit and 32-bit
		uint16_t* stack = (uint16_t*) FP_TO_LINEAR( 0x20, r->useresp );
		uint32_t* stack32 = (uint32_t*) stack;
		
		// instruction pointer
		uint8_t* ip = (uint8_t*) FP_TO_LINEAR( r->cs, r->eip );
		
		// index of execution
		uint32_t exc = 0;
		
		// operand and address sizes
		bool Op32 = false, Addr32 = false;
		
		// safe op?
		bool SafeOp = true;
		
		// knock out any extra stuff at the front
		while( SafeOp )
		{
			switch( ip[0] )
			{
				// 32-bit operand
				case 0x66:
				
					// we are using 32-bit operands
					Op32 = true;
					
					// increment the IP
					ip++;
					
					// set the new EIP in the stack
					r->eip++;
					
					break;
				
				// 32-bit address
				case 0x67:
				
					// 32-bit addresses
					Addr32 = true;
					
					// increment the IP
					ip++;
					
					// set the new EIP in the stack
					r->eip++;
					
					break;

				// PUSHF
				case 0x9C:
				
					// check for 32-bit
					if( Op32 )
					{
						// decrement the stack by 4 bytes
						r->useresp = ( ( r->useresp & 0xFFFF ) - 4 ) & 0xFFFF;
						
						// decrement the stack
						stack32--;
						
						// put the flags on the stack
						stack32[0] = r->eflags & 0xDFF;
						
						// see if we have interrupts enabled
						if( r->eflags & 0x200 )
							stack32[0] |= 0x200; // enable in flags
						else
							stack32[0] ^= 0x200; // disable
					}
					else
					{
						// decrement the stack by 2 bytes
						r->useresp = ( ( r->useresp & 0xFFFF ) - 2 ) & 0xFFFF;
						
						// decrement the stack
						stack--;
						
						// put the flags on the stack
						stack[0] = r->eflags;
						
						// see if we have interrupts enabled
						if( r->eflags & 0x200 )
							stack[0] |= 0x200; // enable in flags
						else
							stack[0] ^= 0x200; // disable
					}
						
					// increment eip
					r->eip++;
					
					// go back to task
					return;
				
				// POPF
				case 0x9D:
				
					// check for 32-bit
					if( Op32 )
					{
						// set the EFLAGS
						r->eflags = 0x20200 | ( stack32[0] & 0xDDF );
						
						// set the new stack pointer
						r->useresp = ( ( r->useresp & 0xFFFF ) + 4 ) & 0xFFFF;
					}
					else
					{
						// set the EFLAGS
						r->eflags = 0x20200 | stack[0];
						
						// set the new stack pointer
						r->useresp = ( ( r->useresp & 0xFFFF ) + 2 ) & 0xFFFF;
					}
					
					// increment eip
					r->eip++;
					
					// return to the task
					return;
				
				// CLI
				case 0xFA:
				
					// take out the interrupt flag
					if( r->eflags & 0x200 )
						r->eflags ^= 0x200;
					
					// increment eip
					r->eip++;
					
					// return to the task
					return;
				
				// STI
				case 0xFB:
				
					// put in the interrupt flag
					if( ( r->eflags & 0x200 ) == 0 )
						r->eflags &= 0x200;
					
					// increment eip
					r->eip++;
					
					// return to the task
					return;
					
				// 0xCD: INT n
				case 0xCD:
				
					// int80 = kill current task
					if( ip[1] == 0x80 )
					{						
						// set the current task's registers
						SetRegs( currpid(), r );
						
						// kill it
						kill( currpid() );
						
						// -- kmcguire: 7/15/2007
						// 'resched' is just a alias for 'sched_switch', but 'sched_switch' will only switch threads by calling
						// 'sched_switch_core' when a certain amount of clocks have passed -- we need to switch _now_ so we call
						// the core function.
						sched_switch_core();
						
						// --kmcguire: 7/15/2007
						// The call to resched has already made another thread ready to run.
						// set the new esp
						// r->esp = current_task[0].esp;
						// increment the eip too
						// r->eip += 2;
						
						// return to the new task
						return;
					}
					
					// decrement the stack 3 times
					stack -= 3;
					
					// put the data on the stack
					stack[0] = (uint16_t) ( r->eip + exc + 2 );
					stack[1] = r->cs;
					stack[2] = (uint16_t) r->eflags;
					
					// set the new stack pointer
					r->useresp = ( ( r->useresp & 0xFFFF ) - 6 ) & 0xFFFF;
					
					// set eflags
					if( r->eflags & 0x200 )
						r->eflags ^= 0x200;
					
					// jump to the necessary location
					r->cs = ivt[ ip[exc+1] * 2 + 1 ];
					r->eip = ivt[ ip[exc+1] * 2 ];
					
					// return, this will take us to the interrupt handler
					return;
				
				// IRET
				case 0xCF:
				
					// set EFLAGS, CS and EIP, then refresh Ring3ESP
					r->eip = stack[0];
					r->cs = stack[1];
					r->eflags = stack[2] | 0x20200;
					
					// new stack pointer
					r->useresp = ( ( r->useresp & 0xFFFF ) + 6 ) & 0xFFFF;
					
					// return to the new place
					return;

				default:
				
					// oops!
					kprintf( "Unknown opcode: 0x%x!\n", ip[0] );
					
					// break free from the loop
					SafeOp = false;
			}
		}
		
		// loop forever - dud op
		while( true );
	}
	
	// syscall?
	if( r->int_no == 0x80 )
	{
		// run the syscall
		SysCall( r );
	}
	else
	{
		// tell me info
		kprintf( "\nI've been rudely interrupted by the processor with this message for you:\n%s\n", exception_messages[r->int_no] );
		
		// -kmcguire: 7/15/2007 (added padding using zeros instead of spaces for numerical values)
		// print the registers
		kprintf( "Crash at 0x%08x\tEFLAGS: 0x%08x\n", r->eip, r->eflags );
		kprintf( "Value of EIP at crash: 0x%08x\n", *((uint_t*)r->eip) );
		kprintf( "Registers at time of crash:\n" );
		kprintf( "Error code: %d\nInterrupt number: %d\n", r->err_code, r->int_no );
		kprintf( "EAX: 0x%08x\tEBX: 0x%08x\tECX: 0x%08x\tEDX: 0x%08x\n", r->eax, r->ebx, r->ecx, r->edx );
		kprintf( "EBP: 0x%08x\tESP: 0x%08x\tESI: 0x%08x\tEDI: 0x%08x\n", r->ebp, r->esp, r->esi, r->edi );
		if( r->eflags & 0x20000 )
		{
			kprintf( "V8086 SS:ESP\n" );
			kprintf( "SS: 0x%08x\tESP: 0x%08x\n", r->ss, r->useresp );
		}
		kprintf( "Segments at time of crash:\n" );
		kprintf( "CS: 0x%08x\tDS: 0x%08x\tES: 0x%08x\nFS: 0x%08x\tGS: 0x%08x\tSS: 0x%08x\n", r->cs, r->ds, r->es, r->fs, r->gs, r->ss );
		kprintf( "Control registers:\n" );
		kprintf( "CR0: 0x%08x\tCR2: 0x%08x\tCR3: 0x%08x\n", read_cr0(), read_cr2(), read_cr3() );
	
		// --kmcguire: 7/15/2007
		// if we are running threads right now, we should be able to continue running them.
		if(____sched_curthread != 0)
		{
			// we are running in a thread; lets try to call the scheduler and let it switch us to another thread.
			kprintf("--kmcguire: resuming next thread (killing current).\n");
			kill( currpid() );
			// 'resched' is just a alias for 'sched_switch', but 'sched_switch' will only switch threads by calling
			// 'sched_switch_core' when a certain amount of clocks have passed -- we need to switch _now_ so we call the core function.
			sched_switch_core();
			return;
		}
	
		// loop forever, we've crashed in the kernel
		while( true );
	}
}
